/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.panomedic.colors;

/**
 *
 * @author Yare
 */
public class ColorConvertor {
    
    public final static int[] rangeLAB = new int[]{100,255,255};
    public static final double[] rangeLCh = new double[]{100,200,360};
    public final static int[] rangeRGB = new int[]{255,255,255};
    public static final double[] rangeYxy = new double[]{100, 1, 1};
    
    //  D65, 2 deg.
    private final static double ref_X = 95.047;
    private final static double ref_Y = 100.00;
    private final static double ref_Z = 108.883;
    
    
    public static double[] RGBtoXYZ(int[] rgba) {
        double var_R = rgba[0] / 255.;
        double var_G = rgba[1] / 255.;
        double var_B = rgba[2] / 255.;


        if (var_R > 0.04045) {
            var_R = Math.pow(((var_R + 0.055) / 1.055), 2.4);
        } else {
            var_R = var_R / 12.92;
        }
        if (var_G > 0.04045) {
            var_G = Math.pow(((var_G + 0.055) / 1.055), 2.4);

        } else {
            var_G = var_G / 12.92;

        }
        if (var_B > 0.04045) {
            var_B = Math.pow(((var_B + 0.055) / 1.055), 2.4);

        } else {
            var_B = var_B / 12.92;
        }

        var_R = var_R * 100;
        var_G = var_G * 100;
        var_B = var_B * 100;

        double xyz[] = new double[3];
        xyz[0] = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805;
        xyz[1] = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722;
        xyz[2] = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505;
        return xyz;
    }
    
    public static double[] XYZtoLAB(double[] xyz) {
        double var_X = xyz[0] / ref_X;
        double var_Y = xyz[1] / ref_Y;
        double var_Z = xyz[2] / ref_Z;

        if (var_X > 0.008856) {
            var_X = Math.pow(var_X, 1 / 3.);
        } else {
            var_X = (7.787 * var_X) + (16 / 116.);
        }
        if (var_Y > 0.008856) {
            var_Y = Math.pow(var_Y, (1 / 3.));
        } else {
            var_Y = (7.787 * var_Y) + (16 / 116.);
        }
        if (var_Z > 0.008856) {
            var_Z = Math.pow(var_Z, (1 / 3.));
        } else {
            var_Z = (7.787 * var_Z) + (16 / 116.);
        }
        double[] lab = new double[3];
        lab[0] = (double) (116 * var_Y) - 16;
        lab[1] = (double) (500 * (var_X - var_Y));
        lab[2] = (double) (200 * (var_Y - var_Z));
        return lab;
    }
    
    public static double[] RGBtoLAB(int[] rgba) {
        double[] result = RGBtoXYZ(rgba);
        result = XYZtoLAB(result);
        return result;
    }
    public static int[] LABtoRGB(double[] lab) {
        double[] result = LABtoXYZ(lab);
        int[] result2 = XYZtoRGB(result);
        return result2;
    }
    public static double[] RGBtoLCh(int[] rgba) {
        double[] result = RGBtoLAB(rgba);
        result = LABtoLCh(result);
        return result;
    }
    public static int[] LChtoRGB(double[] lab) {
        double[] result = LChtoLAB(lab);
        int[] result2 = LABtoRGB(result);
        return result2;
    }

    public static double[] RGBtoYxy(int[] rgba) {
        double[] result = RGBtoXYZ(rgba);
        result = XYZtoYxy(result);
        return result;
    }
    public static int[] YxytoRGB(double[] lab) {
        double[] result = YxytoXYZ(lab);
        int[] result2 = XYZtoRGB(result);
        return result2;
    }
    public static double[] XYZtoYxy(double[] xyz){
        double[] yxy = new double[3];
        yxy[0] = xyz[1];
        if ( (xyz[0] + xyz[1] + xyz[2]) != 0) {
            yxy[1] = xyz[0] / (xyz[0] + xyz[1] + xyz[2]);
            yxy[2] = xyz[1] / (xyz[0] + xyz[1] + xyz[2]);
        } else {
            yxy[1] = 0;
            yxy[2] = 0;
        }
        return yxy;
    }
    
    public static double[] YxytoXYZ(double[] yxy){
        double[] xyz = new double[3];
        xyz[1] = yxy[0];
        if (yxy[2] != 0 ) {
            xyz[0] = yxy[1] * (yxy[0] / yxy[2]);
            xyz[2] = (1 - yxy[1] - yxy[2]) * (yxy[0] / yxy[2]);
        } else {
            xyz[0] = 0;
            xyz[2] = 0;
        }
        
        return xyz;
    }
    
    
    public static double[] LABtoXYZ(double[] lab) {
        double var_Y = (lab[0] +16) / 116d;
        double var_X = lab[1] / 500d + var_Y;
        double var_Z = var_Y - lab[2] / 200d;
        
        double powY = Math.pow(var_Y,3);
        if ( powY > 0.008856) {
            var_Y = powY;
        } else {
            var_Y = (var_Y - 16 / 116d) / 7.787d;
        }
        double powX = Math.pow(var_X,3);
        if (powX > 0.008856) {
            var_X = powX;
        } else {
            var_X = (var_X - 16 / 116d) / 7.787d;
        }
        double powZ = Math.pow(var_Z,3);
        if (powZ > 0.008856) {
            var_Z = powZ;
        } else {
            var_Z = (var_Z - 16 / 116d) / 7.787d;
        }
        double xyz[] = new double[3];
        xyz[0] = ref_X * var_X;
        xyz[1] = ref_Y * var_Y;
        xyz[2] = ref_Z * var_Z;
        return xyz;
    }
    
   
        
    public static int[] XYZtoRGB(double[] xyz)   { 

        double var_X = xyz[0] / 100;
        double var_Y = xyz[1] / 100;
        double var_Z = xyz[2] / 100;
        
        double var_R = var_X * 3.2406 + var_Y * -1.5372 + var_Z * -0.4986;
        double var_G = var_X * -0.9689 + var_Y * 1.8758 + var_Z * 0.0415;
        double var_B = var_X * 0.0557 + var_Y * -0.2040 + var_Z * 1.0570;
 
        if (var_R > 0.0031308) {
            var_R = 1.055 * (Math.pow(var_R, (1 / 2.4))) - 0.055;
        } else {
            var_R = 12.92 * var_R;
        }
        if (var_G > 0.0031308) {
            var_G = 1.055 * (Math.pow(var_G, (1 / 2.4))) - 0.055;
        } else {
            var_G = 12.92 * var_G;
        }
        if (var_B > 0.0031308) {
            var_B = 1.055 * (Math.pow(var_B, (1 / 2.4))) - 0.055;
        } else {
            var_B = 12.92 * var_B;
        }
        
        int rgb[] = new int[3];
        rgb[0] = (int) (var_R * 255);
        rgb[1] = (int) (var_G * 255);
        rgb[2] = (int) (var_B * 255);
        
        return rgb;
        
    }
    
    public static float[] RGBtoLABf(int[] rgba) {
        double var_R = rgba[0] / 255.;
        double var_G = rgba[1] / 255.;
        double var_B = rgba[2] / 255.;

        if (var_R > 0.04045) {
            var_R = Math.pow(((var_R + 0.055) / 1.055), 2.4);

        } else {
            var_R = var_R / 12.92;

        }
        if (var_G > 0.04045) {
            var_G = Math.pow(((var_G + 0.055) / 1.055), 2.4);

        } else {
            var_G = var_G / 12.92;

        }
        if (var_B > 0.04045) {
            var_B = Math.pow(((var_B + 0.055) / 1.055), 2.4);

        } else {
            var_B = var_B / 12.92;
        }

        var_R = var_R * 100;
        var_G = var_G * 100;
        var_B = var_B * 100;

        double X = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805;
        double Y = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722;
        double Z = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505;

        double var_X = X / ref_X;
        double var_Y = Y / ref_Y;
        double var_Z = Z / ref_Z;

        if (var_X > 0.008856) {
            var_X = Math.pow(var_X, 1 / 3.);

        } else {
            var_X = (7.787 * var_X) + (16 / 116.);

        }
        if (var_Y > 0.008856) {
            var_Y = Math.pow(var_Y, (1 / 3.));

        } else {
            var_Y = (7.787 * var_Y) + (16 / 116.);

        }
        if (var_Z > 0.008856) {
            var_Z = Math.pow(var_Z, (1 / 3.));

        } else {
            var_Z = (7.787 * var_Z) + (16 / 116.);
        }


        float[] lab = new float[3];
        lab[0] = (float)(116 * var_Y) - 16;
        lab[1] = (float)(500 * (var_X - var_Y));
        lab[2] = (float)(200 * (var_Y - var_Z));

        return lab;

    }
    
    
    public static int[] LABtoRGBf(float[] lab) {

        // cIELab -> XYZ
        double var_Y = (lab[0] +16) / 116;
        double var_X = lab[1] / 500 + var_Y;
        double var_Z = var_Y - lab[2] / 200;
        
        double powY = Math.pow(var_Y,3);
        if ( powY > 0.008856) {
            var_Y = powY;
        } else {
            var_Y = (var_Y - 16 / 116) / 7.787;
        }
        double powX = Math.pow(var_X,3);
        if (powX > 0.008856) {
            var_X = powX;
        } else {
            var_X = (var_X - 16 / 116) / 7.787;
        }
        double powZ = Math.pow(var_Z,3);
        if (powZ > 0.008856) {
            var_Z = powZ;
        } else {
            var_Z = (var_Z - 16 / 116) / 7.787;
        }
        
        double X = ref_X * var_X;
        double Y = ref_Y * var_Y;
        double Z = ref_Z * var_Z;
        
        // XYZ -> RGB
        var_X = X / 100;
        var_Y = Y / 100;
        var_Z = Z / 100;
        
        double var_R = var_X * 3.2406 + var_Y * -1.5372 + var_Z * -0.4986;
        double var_G = var_X * -0.9689 + var_Y * 1.8758 + var_Z * 0.0415;
        double var_B = var_X * 0.0557 + var_Y * -0.2040 + var_Z * 1.0570;
 
        if (var_R > 0.0031308) {
            var_R = 1.055 * (Math.pow(var_R, (1 / 2.4))) - 0.055;
        } else {
            var_R = 12.92 * var_R;
        }
        if (var_G > 0.0031308) {
            var_G = 1.055 * (Math.pow(var_G, (1 / 2.4))) - 0.055;
        } else {
            var_G = 12.92 * var_G;
        }
        if (var_B > 0.0031308) {
            var_B = 1.055 * (Math.pow(var_B, (1 / 2.4))) - 0.055;
        } else {
            var_B = 12.92 * var_B;
        }
        
        int rgb[] = new int[3];
        rgb[0] = (int) (var_R * 255);
        rgb[1] = (int) (var_G * 255);
        rgb[2] = (int) (var_B * 255);
        
        return rgb;
        
    }

    public static double[] RGBtoHSV(int[] rgba) {
        double var_R = (rgba[0] / 255d); //RGB from 0 to 255
        double var_G = (rgba[1] / 255d);
        double var_B = (rgba[2] / 255d);

        double var_Min = Math.min(var_R, Math.min(var_G, var_B)); //Min. value of RGB
        double var_Max = Math.max(var_R, Math.max(var_G, var_B)); //Max. value of RGB
        double del_Max = var_Max - var_Min; //Delta RGB value

        double V = var_Max;
        double S;
        double H = 0;

        if (del_Max == 0) //This is a gray, no chroma...
        {
            H = 0; //HSV results from 0 to 1
            S = 0;
        } else //Chromatic data...
        {
            S = del_Max / var_Max;

            double del_R = (((var_Max - var_R) / 6) + (del_Max / 2)) / del_Max;
            double del_G = (((var_Max - var_G) / 6) + (del_Max / 2)) / del_Max;
            double del_B = (((var_Max - var_B) / 6) + (del_Max / 2)) / del_Max;

            if (var_R == var_Max) {
                H = del_B - del_G;
            } else if (var_G == var_Max) {
                H = (1 / 3) + del_R - del_B;
            } else if (var_B == var_Max) {
                H = (2 / 3) + del_G - del_R;
            }
            if (H < 0) {
                H += 1;
            }
            if (H > 1) {
                H -= 1;
            }
        }
        return new double[]{H*360,S*100,V*100};
    }

    public static int[] HSVtoRGB(double[] hsv) {
        hsv[0] /= 360;
        hsv[1] /= 100;
        hsv[2] /= 100;
        double R;
        double G;
        double B;
        
        if (hsv[1] == 0) //HSV from 0 to 1
        {
            R = hsv[2] * 255;
            G = hsv[2] * 255;
            B = hsv[2] * 255;
        } else {
            double var_h = hsv[0] * 6;
            if (var_h == 6) {
                var_h = 0;    //H must be < 1
            }
            double var_i = (int) var_h;            //Or ... var_i = floor( var_h )
            double var_1 = hsv[2] * (1 - hsv[1]);
            double var_2 = hsv[2] * (1 - hsv[1] * (var_h - var_i));
            double var_3 = hsv[2] * (1 - hsv[1] * (1 - (var_h - var_i)));

            double var_r;
            double var_g;
            double var_b;
            if (var_i == 0) {
                var_r = hsv[2];
                var_g = var_3;
                var_b = var_1;
            } else if (var_i == 1) {
                var_r = var_2;
                var_g = hsv[2];
                var_b = var_1;
            } else if (var_i == 2) {
                var_r = var_1;
                var_g = hsv[2];
                var_b = var_3;
            } else if (var_i == 3) {
                var_r = var_1;
                var_g = var_2;
                var_b = hsv[2];
            } else if (var_i == 4) {
                var_r = var_3;
                var_g = var_1;
                var_b = hsv[2];
            } else {
                var_r = hsv[2];
                var_g = var_1;
                var_b = var_2;
            }

            R = var_r * 255;                  //RGB results from 0 to 255
            G = var_g * 255;
            B = var_b * 255;
        }
        return new int[]{ (int)Math.round(R),(int)Math.round(G),(int)Math.round(B) };
    }

    private static double[] LABtoLCh(double[] lab) {
        double[] result = new double[3];
        result[2] = Math.atan2(lab[2], lab[1]);
        if (result[2] > 0)  result[2] = ( result[2] / Math.PI ) * 180;
        else            result[2] = 360 - ( Math.abs(result[2])/ Math.PI ) * 180;
        result[0] = lab[0];
        result[1] = Math.sqrt(Math.pow(lab[1], 2)+Math.pow(lab[2], 2));
        return result;
    }
    private static double[] LChtoLAB(double[] lch) {
        double[] result = new double[3];
        result[0] = lch[0];
        result[1] = Math.cos(Math.toRadians(lch[2])) * lch[1];
        result[2] = Math.sin(Math.toRadians(lch[2])) * lch[1];
        return result;
    }
    
    
    
    public static void main(String[] args){
        int[] test = new int[]{24,24,22,0};
        double[] result = RGBtoXYZ(test);
        System.out.println("Result: " + result[0]+"  "+result[1]+"  "+result[2]);
        result = XYZtoLAB(result);
        System.out.println("Result: " + result[0]+"  "+result[1]+"  "+result[2]);
        result = LABtoXYZ(result);
        System.out.println("Result: " + result[0]+"  "+result[1]+"  "+result[2]);
        test = XYZtoRGB(result);
        System.out.println("Result: " + test[0]+"  "+test[1]+"  "+test[2]);
       
        
        
        
//        double[] test2 = new double[]{8.177, -0.503, 1.381};
//        System.out.println("Result: " + test2[0]+"  "+test2[1]+"  "+test2[2]);
//        int[] result2 = LABtoRGB(test2);
//        System.out.println("Result: " + result2[0]+"  "+result2[1]+"  "+result2[2]);
        
    }

            
    
}
